/*
 * Copyright (C) 2023 KylinSoftCo., Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 **/
#include "loginoptionswidget.h"

#include <pwd.h>
#include <unistd.h>

#include <opencv2/opencv.hpp>
#include "giodbus.h"
#include "generic.h"
#include "biotypes.h"

#include <QLabel>
#include <QPainterPath>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QButtonGroup>
#include <QToolButton>
#include <QSizePolicy>
#include <QTimer>
#include <QPixmap>
#include <QPainter>
#include <QtSvg/QSvgRenderer>
#include <QImage>
#include <QApplication>
#include <QFont>
#include <QDebug>


LoginOptionsWidget::LoginOptionsWidget(QWidget *parent)
    : QWidget(parent)
    , m_biomericProxy(new BioAuth(this))
    , m_bioDevices(new BioDevices())
    , w_timer(nullptr)
{
    m_bioDevices->recordAuthDrive("polkit",0,false);
    initUI();
    initConnections();
    m_mapDisableDev.clear();
}

LoginOptionsWidget::~LoginOptionsWidget()
{

}

void LoginOptionsWidget::initUI()
{
    // 初始化UI
    m_layoutMain = new QVBoxLayout();
    m_layoutMain->setContentsMargins(0,0,0,30);
    m_layoutMain->setSpacing(12);
    m_layoutMain->setAlignment(Qt::AlignTop);
    m_layoutOptBtns = new QHBoxLayout();
    m_layoutOptBtns->setContentsMargins(0,0,0,0);
    m_layoutOptBtns->setSpacing(16);
    m_layoutImage = new QHBoxLayout();
    m_layoutImage->setContentsMargins(0,0,0,0);
    m_layoutImage->setSpacing(0);
    m_layoutImage->setAlignment(Qt::AlignCenter);

    m_labelOptTitle = new QLabel();
    m_labelOptTitle->setAlignment(Qt::AlignLeft);
    m_labelOptTitle->setText(tr("Login Options"));
    m_layoutMain->addWidget(m_labelOptTitle);

    m_btnGroup = new QButtonGroup(this);
    m_btnGroup->setExclusive(true);

    m_layoutOptBtns->setAlignment(Qt::AlignLeft);
    m_layoutMain->addLayout(m_layoutOptBtns);

    m_widgetImage = new QWidget();
    m_widgetImage->setFixedSize(154,154);
    m_layoutImage->addSpacerItem(new QSpacerItem(109, 0, QSizePolicy::Expanding));
    m_layoutImage->addWidget(m_widgetImage);
    m_layoutImage->addSpacerItem(new QSpacerItem(109, 0, QSizePolicy::Expanding));
    m_widgetImage->hide();
    // 人脸识别
    m_labelFace = new QLabel(m_widgetImage);
    m_labelFace->setObjectName("faceLabel");
    m_labelFace->setAlignment(Qt::AlignCenter);
    m_labelFace->setFixedSize(154,154);
    QHBoxLayout *layoutFace = new QHBoxLayout();
    layoutFace->setContentsMargins(0,0,0,0);
    layoutFace->setAlignment(Qt::AlignCenter);
    m_labelFace->setLayout(layoutFace);
    m_labelFaceLoad = new QLabel();
    m_labelFaceLoad->setAlignment(Qt::AlignCenter);
    m_labelFaceLoad->setFixedSize(142, 142);
    m_labelFaceLoad->setStyleSheet(QString("QLabel{background-color: rgba(230,230,230,0.39); border-radius: %1px; border: 8px solid white;}").arg(71));
    layoutFace->addWidget(m_labelFaceLoad, 0, Qt::AlignVCenter);


//    QImage img;
//    setFaceImg(img);
    m_labelFace->hide();
    // 二维码窗口
    m_labelQRCode = new QLabel(m_widgetImage);
    m_labelQRCode->setObjectName("qrCodeLabel");
    m_labelQRCode->setAlignment(Qt::AlignCenter);
    m_labelQRCode->setFixedSize(154,154);
    m_labelQRCode->hide();
    m_layoutMain->addLayout(m_layoutImage);
    QVBoxLayout *layoutQRCode = new QVBoxLayout();
    layoutQRCode->setAlignment(Qt::AlignCenter);
    layoutQRCode->setSpacing(5);
    m_labelQRCode->setLayout(layoutQRCode);
    m_labelQRCodeTip = new QLabel();
    m_labelQRCodeTip->setFixedSize(22,22);
    m_labelQRCodeTip->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(QSize(22,22)));
    layoutQRCode->addWidget(m_labelQRCodeTip, 0, Qt::AlignHCenter);
    m_labelQRCodeMsg = new QLabel();
    m_labelQRCodeMsg->setFixedHeight(24);
    QFont font = m_labelQRCodeMsg->font();
    font.setPixelSize(16);
    m_labelQRCodeMsg->setStyleSheet("QLabel{background-color:rgba(255,255,255,0);color:rgb(255,0,0)}");
    m_labelQRCodeMsg->setFont(font);
    layoutQRCode->addWidget(m_labelQRCodeMsg, 0, Qt::AlignHCenter);

    this->setLayout(m_layoutMain);

    //设置水平扩展
    QSizePolicy sizePolicy = this->sizePolicy();
    sizePolicy.setHorizontalPolicy(QSizePolicy::Expanding);
    this->setSizePolicy(sizePolicy);
}

void LoginOptionsWidget::SetExtraInfo(QString extra_info, QString info_type)
{
    if(!m_biomericProxy)
    {
        qWarning() << "BiometricProxy doesn't exist.";
        return;
    }
    m_biomericProxy->SetExtraInfo(info_type,extra_info);
}

void LoginOptionsWidget::initConnections()
{
    if(m_biomericProxy) {
        connect(m_biomericProxy, &BioAuth::notifyDetail,
                this, &LoginOptionsWidget::onStatusChanged);

        connect(m_biomericProxy, &BioAuth::frameWritten,
                this, &LoginOptionsWidget::onFrameWritten);

        connect(m_biomericProxy, &BioAuth::authComplete,
                this, &LoginOptionsWidget::onIdentifyComplete);

        connect(m_biomericProxy, &BioAuth::authFinished,
                this, &LoginOptionsWidget::onAuthFinished);
    }
    if (m_bioDevices) {
        connect(m_bioDevices, &BioDevices::deviceCountChanged,
                this, &LoginOptionsWidget::onUSBDeviceCountChange);
        //readDevicesInfo();
    }
    connect(m_btnGroup, SIGNAL(buttonClicked(int)), this, SLOT(onOptionSelected(int)));
}

bool LoginOptionsWidget::getCurLoginOpt(int& nLoginOptType, int& nDrvId)
{
    if (m_curDevInfo) {
        nLoginOptType = convertDeviceType(m_curDevInfo->biotype);
        nDrvId = m_curDevInfo->device_id;
        return true;
    }
    return false;
}

unsigned LoginOptionsWidget::getLoginOptCount()
{
    return m_mapDevices.size();
}

DeviceInfoPtr LoginOptionsWidget::getFirstDevInfo()
{
    DeviceInfoPtr devInfo = nullptr;
    struct passwd* pwdInfo = getpwuid(m_uid);
    if (!pwdInfo)
        return devInfo;
    int nDrvId = m_bioDevices->GetLastDevice(pwdInfo->pw_name);
    if (nDrvId >= 0) {
        qDebug()<<"GetLastDevice:"<<nDrvId;
        DeviceMap::iterator itDevInfo = m_mapDevices.begin();
        for (; itDevInfo != m_mapDevices.end(); itDevInfo++) {
            for (auto devinfo : itDevInfo.value()) {
                if (devinfo && devinfo->device_id == nDrvId) {
                    if (!isDeviceDisable(devinfo->device_id)) {
                        devInfo =  devinfo;
                        break;
                    }
                }
            }
            if (devInfo) {
                break;
            }
        }
    }
    if (!devInfo) {
        DeviceMap::iterator itDevInfo = m_mapDevices.begin();
        for (; itDevInfo != m_mapDevices.end(); itDevInfo++) {
            for (auto devinfo : itDevInfo.value()) {
                if (devinfo && !isDeviceDisable(devinfo->device_id)) {
                    devInfo =  devinfo;
                    break;
                }
            }
            if (devInfo) {
                break;
            }
        }
    }
    return devInfo;
}

bool LoginOptionsWidget::getHasUkeyOptions()
{
    return isShowUkey;
}

void LoginOptionsWidget::addOptionButton(unsigned uLoginOptType, int nDrvId, QString strDrvName)
{
    if (m_mapOptBtns.contains(nDrvId)) {
        return ;
    }

    QToolButton *newButton = new QToolButton();
//    newButton->setStyleSheet(QString("QToolButton{text-align:center;border: none;border-radius: 6px;outline: none;background-color: rgba(230, 230, 230, 100%);} \
//				      QToolButton::hover{background-color: rgba(55, 144, 250, 40%);}  \
//				      QToolButton::pressed {background-color: rgba(55, 144, 250, 100%);} \
//				      QToolButton::checked {background-color: rgba(55, 144, 250, 100%);} \
//				      "));
    newButton->setCheckable(true);
    newButton->setChecked(false);
    newButton->setFocusPolicy(Qt::NoFocus);
    newButton->setContentsMargins(16,16,16,16);
//    if (nDrvId == -1) {
//        newButton->setEnabled(false);
//        newButton->setChecked(true);
//    } else {
       int nLength = m_btnGroup->buttons().length();
       m_btnGroup->addButton(newButton, nLength);
       m_listDriveId.append(nDrvId);
//   }
    QIcon icon;
    switch (uLoginOptType) {
    case LOGINOPT_TYPE_PASSWORD:
        icon = QIcon(QString("%1/images/ukui-loginopt-password.svg").arg(GET_STR(UKUI_BIOMETRIC)));
        break;
    case LOGINOPT_TYPE_GENERAL_UKEY:
        icon = QIcon(QString("%1/images/ukui-loginopt-ukey.svg").arg(GET_STR(UKUI_BIOMETRIC)));
        break;
    case LOGINOPT_TYPE_FACE:
        icon = QIcon(QString("%1/images/ukui-loginopt-face.svg").arg(GET_STR(UKUI_BIOMETRIC)));
        break;
    case LOGINOPT_TYPE_FINGERPRINT:
        icon = QIcon(QString("%1/images/ukui-loginopt-finger.svg").arg(GET_STR(UKUI_BIOMETRIC)));
        break;
    case LOGINOPT_TYPE_IRIS:
        icon = QIcon(QString("%1/images/ukui-loginopt-iris.svg").arg(GET_STR(UKUI_BIOMETRIC)));
        break;
    case LOGINOPT_TYPE_VOICEPRINT:
        icon = QIcon(QString("%1/images/ukui-loginopt-voice.svg").arg(GET_STR(UKUI_BIOMETRIC)));
        break;
    case LOGINOPT_TYPE_FINGERVEIN:
        icon = QIcon(QString("%1/images/ukui-loginopt-fingervein.svg").arg(GET_STR(UKUI_BIOMETRIC)));
        break;
    case LOGINOPT_TYPE_QRCODE:
        icon = QIcon(QString("%1/images/ukui-loginopt-qrcode.svg").arg(GET_STR(UKUI_BIOMETRIC)));
        break;
    }
    newButton->setIcon(icon);
    newButton->setToolTip(strDrvName);
    newButton->setFixedSize(36, 36);
    if (isDeviceDisable(nDrvId)) {
        newButton->setDisabled(true);
    } else {
        newButton->setDisabled(false);
    }

    m_layoutOptBtns->addWidget(newButton);
    m_mapOptBtns[nDrvId] = newButton;
}

void LoginOptionsWidget::clearOptionButtons()
{
    QMap<int, QToolButton*>::iterator itMapBtn = m_mapOptBtns.begin();
    for ( ; itMapBtn != m_mapOptBtns.end(); itMapBtn++) {
        if (itMapBtn.value()) {
            m_btnGroup->removeButton(itMapBtn.value());
            m_layoutOptBtns->removeWidget(itMapBtn.value());
            itMapBtn.value()->deleteLater();
        }
    }
    m_listDriveId.clear();
    m_mapOptBtns.clear();
}

void LoginOptionsWidget::updateOptionButtons()
{
    isShowUkey = false;
    clearOptionButtons();
    addOptionButton(LOGINOPT_TYPE_PASSWORD, -1, tr("Password"));
    DeviceMap::iterator itMapDev = m_mapDevices.begin();
    for ( ; itMapDev != m_mapDevices.end(); itMapDev++) {
        for (DeviceInfoPtr devPtr : itMapDev.value()) {
            if (devPtr) {
                if(devPtr->biotype == LOGINOPT_TYPE_GENERAL_UKEY){
                    //ukey 设备类型排在二维码前，但实际上应该显示在二维码之后，因此暂时不添加
                    isShowUkey = true;
                    continue;
                }
	        addOptionButton(itMapDev.key(), devPtr->device_id, BioDevices::bioTypeToString_tr(devPtr->biotype));
	    }
        }
    }

    itMapDev = m_mapDevices.begin();
    if(isShowUkey){
        for ( ; itMapDev != m_mapDevices.end(); itMapDev++) {
            for (DeviceInfoPtr devPtr : itMapDev.value()) {
                if(devPtr && devPtr->biotype == LOGINOPT_TYPE_GENERAL_UKEY){
                    //此处才添加ukey
                    addOptionButton(itMapDev.key(), devPtr->device_id, BioDevices::bioTypeToString_tr(devPtr->biotype));
		}
            }
        }
    }

    //存在特征但没有插入ukey
    if( !isShowUkey && m_biomericProxy->GetHasUkeyFeature(m_uid)){
        addOptionButton(LOGINOPT_TYPE_GENERAL_UKEY,-2,BioDevices::bioTypeToString_tr(LOGINOPT_TYPE_GENERAL_UKEY));
        isShowUkey = true;
    }

    if (m_mapOptBtns.size() <= 2 && !isShowUkey) {
        //因为默认添加一个密码选项，因此当ukey没有显示出来时，按钮数小于等于2时就隐藏选项界面
        m_labelOptTitle->hide();
        QMap<int, QToolButton*>::iterator itMapBtn = m_mapOptBtns.begin();
        for ( ; itMapBtn != m_mapOptBtns.end(); itMapBtn++) {
            if (itMapBtn.value()) {
                itMapBtn.value()->hide();
            }
        }
        if (m_mapOptBtns.size() == 0) {
            m_labelFace->hide();
            m_labelQRCode->hide();
        }
    } else {
        m_labelOptTitle->show();
        QMap<int, QToolButton*>::iterator itMapBtn = m_mapOptBtns.begin();
        for ( ; itMapBtn != m_mapOptBtns.end(); itMapBtn++) {
            if (itMapBtn.value()) {
                itMapBtn.value()->show();
            }
        }
    }

    m_mapOptBtns[-1]->hide();
    if(m_mapOptBtns.size() == 2 && isShowUkey) {
        m_mapOptBtns[-1]->show();
    }

    adjustSize();
}

void LoginOptionsWidget::setUser(int uid)
{
    bool bNeedUpdateDevInfo = false;
    if (uid != m_uid) {
        bNeedUpdateDevInfo = true;
    }
    m_uid = uid;
    if (bNeedUpdateDevInfo) {
        if (m_bioDevices) {
            m_bioDevices->setUId(m_uid);
        }
        readDevicesInfo();
    }
}

void LoginOptionsWidget::readDevicesInfo()
{
    m_mapDevices.clear();
    bool isAuthEnable = m_bioDevices->GetBioAuthEnable();
    bool isQRCodeEnable = m_bioDevices->GetQRCodeEnable();
    DeviceList deviceList = m_bioDevices->GetDevList();
    QStringList listDefDevices = m_bioDevices->getAllDefDevices();
    FeatureMap mapFeatures = m_bioDevices->GetUserFeatures(m_uid);
        qDebug() << m_uid <<",count:"<<mapFeatures.size();

    for(auto pDeviceInfo : deviceList)
    {
        int nDevFeatureCount = 0;
        if (m_isInAuth && m_curDevInfo && m_curDevInfo->device_id == pDeviceInfo->device_id) {
            nDevFeatureCount = 1;
        } else {
            if (m_uid == -1) {
                nDevFeatureCount = 0;
            } else if(mapFeatures.contains(pDeviceInfo->device_shortname)){
                nDevFeatureCount = mapFeatures[pDeviceInfo->device_shortname].size();
            }
        }
        qDebug() << *pDeviceInfo << ",FeatureCount:"<<nDevFeatureCount;
        if(nDevFeatureCount > 0) {
            if (!isAuthEnable && pDeviceInfo->biotype <= BIOTYPE_VOICEPRINT)
                continue;
            if (!isQRCodeEnable && pDeviceInfo->biotype == REMOTE_QRCODE_TYPE)
                continue;
            int nDevType = LOGINOPT_TYPE_OTHERS;
            nDevType = convertDeviceType(pDeviceInfo->biotype);
            if (listDefDevices.contains(pDeviceInfo->device_shortname) &&
               !m_mapDevices.contains(nDevType)) {
                m_mapDevices[nDevType].push_back(pDeviceInfo);
            }
        }
    }
    updateOptionButtons();
    updateUIStatus(false);
    //Q_EMIT notifyOptionsChange(m_mapDevices.size());
}

void LoginOptionsWidget::startAuth(DeviceInfoPtr device, int uid)
{
    if(!device || !m_biomericProxy || is_Lock)
    {
        qWarning() << "BiometricProxy doesn't exist.";
        return;
    }

    if(m_isInAuth)
    {
        qDebug() << "Identification is currently under way, stop it";
        stopAuth();
    }
    qDebug()<<"deviceInfo:"<<device->device_id;
    this->m_curDevInfo = device;
    this->m_uid = uid;
    struct passwd *pwdInfo = getpwuid(uid);
    if (pwdInfo) {
        this->m_strUserName = pwdInfo->pw_name;
    }
    this->m_isStopped = false;
    this->m_curLoginOptType = convertDeviceType(this->m_curDevInfo->biotype);
    updateUIStatus(true);
    if (pwdInfo) {
        m_bioDevices->SetLastDevice(pwdInfo->pw_name, this->m_curDevInfo->device_id);
    }
    startAuth_();
}

void LoginOptionsWidget::setSelectedPassword()
{
    if (m_mapOptBtns.contains(-1)) {
        QToolButton* btn = m_mapOptBtns[-1];
        if (btn && btn->isVisible()) {
            btn->setChecked(true);
        }
        m_curDevInfo = nullptr;
    }
}

void LoginOptionsWidget::startAuth_()
{
    if (!m_curDevInfo)
        return ;
    qDebug().noquote() << QString("Identify:[drvid: %1, uid: %2]").arg(m_curDevInfo->device_id).arg(m_uid);

    m_isInAuth = true;
    m_dupFD = -1;
    if(!w_timer)
    {
        w_timer = new QTimer(this);
        w_timer->setInterval(150);
        connect(w_timer, &QTimer::timeout, this, &LoginOptionsWidget::updatePixmap);
    }
    m_waitingPixmap = QIcon::fromTheme("ukui-loading-0-symbolic").pixmap(24, 24);
    if(m_curLoginOptType == LOGINOPT_TYPE_QRCODE) {
        m_labelQRCodeTip->setPixmap(m_waitingPixmap);
        m_labelQRCodeTip->show();
        m_labelQRCodeMsg->hide();
    } else if(m_curLoginOptType == LOGINOPT_TYPE_FACE) {
        m_labelFace->setStyleSheet(QString("border-radius: %1px;  border:0px solid white;background-color: rgba(230,230,230,0.39);").arg(77));
        m_labelFace->setPixmap(QPixmap(""));
        m_labelFaceLoad->setPixmap(m_waitingPixmap);
        m_labelFaceLoad->show();
    }
    w_timer->start();
    m_bioDevices->recordAuthDrive("polkit",m_curDevInfo->device_id,true);
    m_biomericProxy->startAuth(m_uid, m_curDevInfo);
}

void LoginOptionsWidget::stopAuth()
{
    m_isStopped = true;
    if(!m_isInAuth || !m_curDevInfo)
    {
        return;
    }
    m_biomericProxy->stopAuth();
    if(m_retrytimer&&m_retrytimer->isActive()){
        m_retrytimer->stop();
        delete m_retrytimer;
        m_retrytimer = nullptr;
    }
    m_isInAuth = false;
    Q_EMIT updateImage(QImage());
    QImage img;
    setFaceImg(img);
}

void LoginOptionsWidget::updatePixmap()
{
    QMatrix matrix;
    matrix.rotate(90.0);
    m_waitingPixmap = m_waitingPixmap.transformed(matrix, Qt::FastTransformation);
    if(m_curLoginOptType == LOGINOPT_TYPE_QRCODE)
        m_labelQRCodeTip->setPixmap(m_waitingPixmap);
    else if(m_curLoginOptType == LOGINOPT_TYPE_FACE) {
        m_labelFaceLoad->setPixmap(m_waitingPixmap);
    }
}

void LoginOptionsWidget::onAuthFinished()
{
    if(m_bioDevices){
        m_bioDevices->recordAuthDrive("polkit",0,false);
    }
}

void LoginOptionsWidget::onIdentifyComplete(int uid, bool ret, int retErrNo)
{
    if(m_isStopped == true)
        return ;

    if(uid < 0)
    {
        readDevicesInfo();
        Q_EMIT authComplete(uid, false, -1);
        return;
    }
    qDebug() << retErrNo << uid << m_uid;

    // 特征识别成功，而且用户id匹配
    if(retErrNo == DBUS_RESULT_SUCCESS && uid == m_uid) {
        qDebug() << "Identify success";
        Q_EMIT authComplete(uid, true, 0);
    } else if(retErrNo == -1) { // 特征识别不匹配
        qDebug() << "Identify failed";
        Q_EMIT authComplete(uid, false, 2);
    } else if(retErrNo == -2) {  //识别发生错误
        StatusReslut ret = m_bioDevices->UpdateStatus(m_curDevInfo->device_id);
        qDebug()<<"StatusReslut:"<<ret.result<<","<<ret.enable<<","<<ret.devNum<<","
                          <<ret.devStatus<<","<<ret.opsStatus<<","<<ret.notifyMessageId;
        //识别操作超时
        if(ret.result == 0 && (ret.opsStatus == 404 || ret.opsStatus == 304
              || ret.opsStatus == 8)) { // 304认证超时， 8网络异常
            Q_EMIT authComplete(uid, false, 1);
        } else if (ret.opsStatus == OPS_IDENTIFY_STOP_BY_USER || ret.opsStatus == OPS_VERIFY_STOP_BY_USER) {
            Q_EMIT authComplete(uid, false, -2); // 主动停止，直接重试
        } else if (ret.opsStatus == OPS_OPEN_FAIL || ret.opsStatus == OPS_OPEN_ERROR) {     // 无法打开设备（设备是坏的/被占用），直接禁用
            Q_EMIT authComplete(uid, false, 5);
        } else {
            Q_EMIT authComplete(uid, false, 2);
        }
    } else {
        Q_EMIT authComplete(uid, false, 2);
    }
}

QPixmap LoginOptionsWidget::PixmapToRound(const QPixmap &src, int radius)
{
    if (src.isNull()) {
        return QPixmap();
    }

    QPixmap pixmapa(src);
    QPixmap pixmap(radius*2,radius*2);
    pixmap.fill(Qt::transparent);
    QPainter painter(&pixmap);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    QPainterPath path;
    path.addEllipse(0, 0, radius*2, radius*2);
    painter.setClipPath(path);
    painter.drawPixmap(0, 0, radius*2, radius*2, pixmapa);
    return pixmap;
}

/**
 * @brief scaledPixmap
 * @param width
 * @param height
 * @param url
 * @return
 * 图片缩放
 */
QPixmap LoginOptionsWidget::scaledPixmap(int width, int height, QString url)
{
    QFile imgFile(url);
    if(!imgFile.exists()){
        qDebug()<< "not find the pixmap file";
        return QPixmap();
    }
    QImage image(url);
    QPixmap pixmap = QPixmap::fromImage(image);
    if(pixmap.isNull()) {
        qDebug() << "pixmap is null";
        QProcess exec;
        QString program("file " + url);
        exec.start(program);
        exec.waitForFinished(1000);
        QString output = exec.readAllStandardOutput();
        qDebug() << output;
        if(output.contains("SVG")){
            qDebug() << "image format is SVG";
            QSvgRenderer render(url);
            QImage image(width, height, QImage::Format_ARGB32);
            image.fill(Qt::transparent);
            QPainter painter(&image);
            render.render(&painter, image.rect());
            pixmap.convertFromImage(image);
        } else if(output.contains("TIFF")) {
            qDebug() << "image format is TIFF";

        }
    }

    return pixmap.scaled(width, height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
}

void LoginOptionsWidget::onFrameWritten(int drvid)
{
    if(!m_curDevInfo)
        return;

    if (m_curDevInfo &&  m_curDevInfo->device_id != drvid || !m_isInAuth) {
        return ;
    }

    if(m_dupFD == -1){
        m_dupFD = get_server_gvariant_stdout(drvid);
    }

    if(m_dupFD <= 0)
        return ;

    cv::Mat img;
    lseek(m_dupFD, 0, SEEK_SET);
    char base64_bufferData[1024*1024];
    int rc = read(m_dupFD, base64_bufferData, 1024*1024);
    Q_UNUSED(rc);
    // printf("rc = %d\n", rc);

    cv::Mat mat2(1, sizeof(base64_bufferData), CV_8U, base64_bufferData);
    img = cv::imdecode(mat2, cv::IMREAD_COLOR);
    if (!img.data)
        return ;
    cv::cvtColor(img,img,cv::COLOR_BGR2RGB);

    QImage srcQImage = QImage((uchar*)(img.data), img.cols, img.rows, QImage::Format_RGB888);
    if (isAuthenticating()) {
        if (m_curLoginOptType == LOGINOPT_TYPE_FACE) {
            m_labelFace->setStyleSheet(QString("border-radius: %1px;  border:0px solid white;background-color: rgba(255,255,255,10%);").arg(77));
            setFaceImg(srcQImage);
            m_labelFace->show();
            m_labelQRCode->hide();
        } else if (m_curLoginOptType == LOGINOPT_TYPE_QRCODE) {
            m_labelQRCode->setStyleSheet(QString("border-radius: %1px;  border:0px solid white;background-color: rgba(255,255,255,100%);").arg(6));
            setQRCode(srcQImage);
            m_labelQRCode->show();
            m_labelQRCodeTip->hide();
            m_labelFace->hide();
        } else {
            m_labelFace->hide();
            m_labelQRCode->hide();
        }
    } else {
        m_labelFace->hide();
        m_labelQRCode->hide();
    }
    if(w_timer && w_timer->isActive())
    {
        w_timer->stop();
    }
    Q_EMIT updateImage(srcQImage);
}

void LoginOptionsWidget::setQRCode(QImage& imgQRCode)
{
    if (imgQRCode.isNull()) {
        imgQRCode.load(QString("%1/images/ukui-qrcode-null.svg").arg(GET_STR(UKUI_BIOMETRIC)));
    }
    imgQRCode = imgQRCode.scaled(QSize(150, 150));
    m_labelQRCode->setAlignment(Qt::AlignCenter);
    m_labelQRCode->setPixmap(QPixmap::fromImage(imgQRCode));
}

void LoginOptionsWidget::setQRCodeMsg(QString strMsg)
{
    if (strMsg.isEmpty()) {
        m_labelQRCodeMsg->hide();
        //m_labelQRCodeTip->hide();
    } else {
        //一开始认证时就没有网，直接停止加载状态
        if(w_timer && w_timer->isActive())
        {
            w_timer->stop();
        }
        m_labelQRCodeMsg->setText(strMsg);
        m_labelQRCodeMsg->show();
        m_labelQRCodeTip->show();
        m_labelQRCodeTip->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(QSize(22,22)));
    }
}

void LoginOptionsWidget::setFaceImg(QImage& imgFace, int nStatus)
{
    QPixmap faceImage;
    m_labelFace->setFixedSize(154,154);
    //如果头像文件不存在，则使用默认头像
    if(!imgFace.isNull()) {
        faceImage = PixmapToRound(QPixmap::fromImage(imgFace),77);
    } else {
        switch(nStatus) {
        case 1:
            faceImage = loadSvg(QString("%1/images/ukui-loginopt-lose.svg").arg(GET_STR(UKUI_BIOMETRIC)),"black",48);
            m_labelFace->setStyleSheet(QString("border-radius: %1px;  border:0px solid white;background-color: rgba(230,230,230,40%);").arg(77));
            break;
        case 2:
            faceImage = loadSvg(QString("%1/images/ukui-loginopt-smile.svg").arg(GET_STR(UKUI_BIOMETRIC)),"gray",48);
            m_labelFace->setStyleSheet(QString("border-radius: %1px;  border:0px solid white;background-color: rgba(230,230,230,100%);").arg(77));
            break;
        default:
            faceImage = loadSvg(QString("%1/images/ukui-loginopt-smile.svg").arg(GET_STR(UKUI_BIOMETRIC)),"black",48);
            m_labelFace->setStyleSheet(QString("border-radius: %1px;  border:0px solid white;background-color: rgba(230,230,230,40%);").arg(77));
            break;
        }
    }

    m_labelFace->setAlignment(Qt::AlignCenter);
    m_labelFace->setPixmap(faceImage);
    m_labelFaceLoad->hide();
}

void LoginOptionsWidget::onStatusChanged(int drvid, const QString &message)
{
    if(!m_isInAuth || !m_curDevInfo)
    {
        return;
    }
    if(drvid != m_curDevInfo->device_id)
    {
        return;
    }
    //Q_EMIT updateAuthMsg(message);
}

void LoginOptionsWidget::setCurrentDevice(int drvid)
{
    DeviceInfoPtr pDeviceInfo = findDeviceById(drvid);
    if(pDeviceInfo)
    {
        setCurrentDevice(pDeviceInfo);
    }
}

void LoginOptionsWidget::setCurrentDevice(const QString &deviceName)
{
    DeviceInfoPtr pDeviceInfo = findDeviceByName(deviceName);
    if(pDeviceInfo)
    {
        setCurrentDevice(pDeviceInfo);
    }
}

void LoginOptionsWidget::setCurrentDevice(const DeviceInfoPtr &pDeviceInfo)
{
    this->m_curDevInfo = pDeviceInfo;
}

DeviceInfoPtr LoginOptionsWidget::findDeviceById(int drvid)
{
    for(int type : m_mapDevices.keys())
    {
        DeviceList &deviceList = m_mapDevices[type];
        auto iter = std::find_if(deviceList.begin(), deviceList.end(),
                                 [&](DeviceInfoPtr ptr){
            return ptr->device_id == drvid;
        });
        if(iter != deviceList.end())
        {
            return *iter;
        }
    }
    return DeviceInfoPtr();
}

DeviceInfoPtr LoginOptionsWidget::findDeviceByName(const QString &name)
{
    for(int type : m_mapDevices.keys())
    {
        DeviceList &deviceList = m_mapDevices[type];
        auto iter = std::find_if(deviceList.begin(), deviceList.end(),
                                 [&](DeviceInfoPtr ptr){
            return ptr->device_shortname == name;
        });
        if(iter != deviceList.end())
        {
            return *iter;
        }
    }
    return DeviceInfoPtr();
}

QString LoginOptionsWidget::GetDefaultDevice(uid_t uid,int bioType)
{
    QString defaultDeviceName = "";
    DeviceInfoPtr pDeviceInfo = m_bioDevices->getDefaultDevice(uid,bioType);
    if (pDeviceInfo) {
        defaultDeviceName = pDeviceInfo->device_shortname;
    }
    qDebug() << "default device: " << defaultDeviceName;

    return defaultDeviceName;
}

QString LoginOptionsWidget::GetDefaultDevice(uid_t uid)
{
    QString defaultDeviceName = "";
    DeviceInfoPtr pDeviceInfo = m_bioDevices->getDefaultDevice(uid);
    if (pDeviceInfo) {
        defaultDeviceName = pDeviceInfo->device_shortname;
    }
    qDebug() << "default device: " << defaultDeviceName;

    return defaultDeviceName;
}

void LoginOptionsWidget::onUSBDeviceCountChange(int newNum)
{
    qDebug()<<"deviceNewNum:"<<newNum;
    int savedCount = 0;
    for(int type : m_mapDevices.keys())
        savedCount += m_mapDevices.value(type).count();

    readDevicesInfo();

    int count = 0;
    for(int type : m_mapDevices.keys())
        count += m_mapDevices.value(type).count();
    
    //设备数量发生了变化
    if(count != savedCount) {
        updateOptionButtons();
        Q_EMIT notifyOptionsChange(count);
    }
    updateUIStatus(false);
}

void LoginOptionsWidget::updateUkeyUIStatus(int type)
{
    if (type == LOGINOPT_TYPE_FACE) {
        m_labelFace->setStyleSheet(QString("border-radius: %1px;  border:0px solid white;background-color: rgba(255,255,255,10%);").arg(77));
        m_labelFace->show();
        m_labelQRCode->hide();
        m_widgetImage->show();
    } else if (type == LOGINOPT_TYPE_QRCODE) {
        m_labelQRCode->setStyleSheet(QString("border-radius: %1px;  border:0px solid white;background-color: rgba(255,255,255,100%);").arg(6));
        QImage img;
        setQRCode(img);
        m_labelQRCode->show();
        m_labelFace->hide();
        m_widgetImage->show();
    } else {
        m_labelFace->hide();
        m_labelQRCode->hide();
        m_widgetImage->hide();
    }

    QTimer::singleShot(0,this,[&,this](){
        Q_EMIT this->updateWndSize(this->m_curLoginOptType, this->m_mapOptBtns.size());
    });
}

void LoginOptionsWidget::updateUIStatus(bool update)
{
    if (m_mapOptBtns.contains(-1)) {
        QToolButton* btn = m_mapOptBtns[-1];
        if (btn && btn->isVisible()) {
            btn->setChecked(true);
        }
    }
    if (m_curDevInfo) {
        if (m_mapOptBtns.contains(m_curDevInfo->device_id)) {
            QToolButton* btn = m_mapOptBtns[m_curDevInfo->device_id];
            if (btn) {
                btn->setChecked(true);
            }
        }
    }

    if (update) {
        if (m_curLoginOptType == LOGINOPT_TYPE_FACE) {
            m_labelFace->setStyleSheet(QString("border-radius: %1px;  border:0px solid white;background-color: rgba(255,255,255,10%);").arg(77));
            m_labelFace->show();
            m_labelQRCode->hide();
            m_widgetImage->show();
        } else if (m_curLoginOptType == LOGINOPT_TYPE_QRCODE) {
            m_labelQRCode->setStyleSheet(QString("border-radius: %1px;  border:0px solid white;background-color: rgba(255,255,255,100%);").arg(6));
            QImage img;
            setQRCode(img);
            m_labelQRCode->show();
            m_labelFace->hide();
            m_widgetImage->show();
        } else {
            m_labelFace->hide();
            m_labelQRCode->hide();
            m_widgetImage->hide();
        }
    }
    QTimer::singleShot(0,this,[&,this](){
        Q_EMIT this->updateWndSize(this->m_curLoginOptType, this->m_mapOptBtns.size());
    });
}

void LoginOptionsWidget::onOptionSelected(int nIndex)
{
    if (nIndex < 0)
        return;
    if (nIndex < m_listDriveId.size()) {
        DeviceInfoPtr info = findDeviceById(m_listDriveId[nIndex]);
        if (info && !isDeviceDisable(info->device_id)) {
            Q_EMIT optionSelected(convertDeviceType(info->biotype), info);
            this->m_curLoginOptType = convertDeviceType(info->biotype);
        }else if(nIndex == 0 && m_listDriveId[nIndex] == -1){
            stopAuth();
            m_curDevInfo = nullptr;
            Q_EMIT optionSelected(LOGINOPT_TYPE_PASSWORD,nullptr);
            this->m_curLoginOptType = LOGINOPT_TYPE_PASSWORD;

        }else if(m_listDriveId[nIndex] == -2){
            //存在ukey特征，但未插入ukey
            stopAuth();
            m_curDevInfo = nullptr;
            Q_EMIT optionSelected(LOGINOPT_TYPE_GENERAL_UKEY,nullptr);
            this->m_curLoginOptType = LOGINOPT_TYPE_GENERAL_UKEY;
        }
    }
}

int LoginOptionsWidget::convertDeviceType(int nDevType)
{
    int nLoginOptType = LOGINOPT_TYPE_OTHERS;
    switch (nDevType) {
    case BIOTYPE_FINGERPRINT:
        nLoginOptType = LOGINOPT_TYPE_FINGERPRINT;
        break;
    case BIOTYPE_FINGERVEIN:
        nLoginOptType = LOGINOPT_TYPE_FINGERVEIN;
        break;
    case BIOTYPE_IRIS:
        nLoginOptType = LOGINOPT_TYPE_IRIS;
        break;
    case BIOTYPE_FACE:
        nLoginOptType = LOGINOPT_TYPE_FACE;
        break;
    case BIOTYPE_VOICEPRINT:
        nLoginOptType = LOGINOPT_TYPE_VOICEPRINT;
        break;
    case UniT_General_Ukey:
        nLoginOptType = LOGINOPT_TYPE_GENERAL_UKEY;
        break;
    case UniT_Advanced_Ukey:
        nLoginOptType = LOGINOPT_TYPE_ADVANCED_UKEY;
        break;
    case REMOTE_QRCODE_TYPE:
        nLoginOptType = LOGINOPT_TYPE_QRCODE;
        break;
    default:
        nLoginOptType = LOGINOPT_TYPE_OTHERS;
        break;
    }
    return nLoginOptType;
}

void LoginOptionsWidget::setDeviceDisable(int nDevId, bool bDisable)
{
    if (bDisable) {
        m_mapDisableDev[m_uid][nDevId] = true;
        QMap<int, QToolButton*>::iterator itMapBtn = m_mapOptBtns.begin();
        for ( ; itMapBtn != m_mapOptBtns.end(); itMapBtn++) {
            if (itMapBtn.key() == nDevId && itMapBtn.value()) {
                itMapBtn.value()->setDisabled(true);
                break;
            }
        }
    } else {
        m_mapDisableDev[m_uid][nDevId] = false;
        QMap<int, QToolButton*>::iterator itMapBtn = m_mapOptBtns.begin();
        for ( ; itMapBtn != m_mapOptBtns.end(); itMapBtn++) {
            if (itMapBtn.key() == nDevId && itMapBtn.value()) {
                itMapBtn.value()->setDisabled(false);
                break;
            }
        }
    }
}

bool LoginOptionsWidget::isDeviceDisable(int nDevId)
{
    if (m_mapDisableDev[m_uid].contains(nDevId)) {
        return m_mapDisableDev[m_uid][nDevId];
    }
    return false;
}

void LoginOptionsWidget::lockStatusChanged(bool locked)
{
    if(locked){
        is_Lock = true;
        stopAuth();
    } else {
        is_Lock = false;
        readDevicesInfo();
    }
}

QPixmap LoginOptionsWidget::loadSvg(QString path, QString color, int size)
{
    int origSize = size;
    const auto ratio = qApp->devicePixelRatio();
    if ( 2 == ratio) {
        size += origSize;
    } else if (3 == ratio) {
        size += origSize;
    }
    QPixmap pixmap(size, size);
    QSvgRenderer renderer(path);
    pixmap.fill(Qt::transparent);

    QPainter painter;
    painter.begin(&pixmap);
    renderer.render(&painter);
    painter.end();

    pixmap.setDevicePixelRatio(ratio);
    return drawSymbolicColoredPixmap(pixmap, color);
}

QPixmap LoginOptionsWidget::drawSymbolicColoredPixmap(QPixmap &source, QString cgColor)
{
    QImage img = source.toImage();
    for (int x = 0; x < img.width(); x++) {
        for (int y = 0; y < img.height(); y++) {
            auto color = img.pixelColor(x, y);
            if (color.alpha() > 0) {
                if ( "white" == cgColor) {
                    color.setRed(255);
                    color.setGreen(255);
                    color.setBlue(255);
                    img.setPixelColor(x, y, color);
                } else if( "black" == cgColor) {
                    color.setRed(0);
                    color.setGreen(0);
                    color.setBlue(0);
                    img.setPixelColor(x, y, color);
                } else if ("gray"== cgColor) {
                    color.setRed(152);
                    color.setGreen(163);
                    color.setBlue(164);
                    img.setPixelColor(x, y, color);
                } else if ("blue" == cgColor){
                    color.setRed(61);
                    color.setGreen(107);
                    color.setBlue(229);
                    img.setPixelColor(x, y, color);
                } else {
                    return source;
                }
            }
        }
    }
    return QPixmap::fromImage(img);
}
